package dao

import (
	"database/sql"
	"errors"
	"fmt"
	"go-init/lib/db/mysql"
	"strconv"
	"strings"

	"github.com/didi/gendry/scanner"
)

type Other struct {
	Having  string
	Offset  int
	Limit   int
	GroupBy string
	OrderBy string
}

func (o Other) Format() (fs string) {
	if o.GroupBy != "" {
		fs += " group by " + o.GroupBy
	}
	if o.Having != "" {
		fs += " having " + o.Having
	}
	if o.OrderBy != "" {
		fs += " order by " + o.OrderBy
	}
	fs += limit(o.Limit, o.Offset)
	return
}

func limit(limit, offset int) string {
	if offset >= 0 {
		return " limit " + strconv.Itoa(offset) + "," + strconv.Itoa(limit)
	}
	return " "
}

// Qs query
type Qs struct {
	where     map[string]interface{}
	sortarray []string
}

func (q *Qs) Add(key string, value ...interface{}) {
	if _, ok := q.where[key]; !ok {
		q.sortarray = append(q.sortarray, key)
	}
	if len(value) == 1 {
		q.where[key] = value[0]
	} else {
		q.where[key] = value
	}
}

func NewQs() *Qs {
	return &Qs{where: make(map[string]interface{})}
}

func (q *Qs) Init() {
	q.where = make(map[string]interface{})
	q.sortarray = []string{}
}

// Format format Qs struct
func (q *Qs) Format() (w string, p []interface{}) {
	var ws []string
	for i := range q.sortarray {
		v, ok := q.where[q.sortarray[i]]
		if ok {
			ws = append(ws, q.sortarray[i])
			if v != false {
				switch t := v.(type) {
				case []interface{}:
					p = append(p, t...)
				case []string:
					for _, tnode := range t {
						fmt.Println(tnode)
						p = append(p, tnode)
					}
				case []int:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				case []int8:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				case []int16:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				case []int32:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				case []int64:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				case []float32:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				case []float64:
					for _, tnode := range t {
						p = append(p, tnode)
					}
				default:
					p = append(p, t)
				}
			}
		}
	}
	if len(ws) > 0 {
		w = " where "
	}
	w += strings.Join(ws, " and ")
	return
}

// Format format Qs struct
func (q *Qs) FormatW() (w string) {
	var ws []string
	for i := range q.sortarray {
		v, ok := q.where[q.sortarray[i]]
		if ok {
			ws = append(ws, q.sortarray[i]+fmt.Sprint(v))
		}
	}
	w = strings.Join(ws, " and ")
	return
}

//DefaultOther
var DefaultOther = Other{Offset: 0, Limit: 1}

var NotFound = errors.New("record not found")

//DefaultLimit is empty string
const DefaultPageSize = 10

func Query(sql string, params ...interface{}) (*sql.Rows, error) {
	return db.Query(sql, params...)
}

func Exec(sql string, params ...interface{}) (sql.Result, error) {
	return db.Exec(sql, params...)
}

func Insert(res interface{}) (int64, error) {
	return db.Insert(res)
}

func Update(res interface{}) (int64, error) {
	return db.Update(res)
}

func InsertOrUpdate(res interface{}) (int64, error) {
	return db.InsertOrUpdate(res)
}

func Begin() (*mysql.Tx, error) {
	return db.Begin()
}

type count struct {
	Num int `gorm:"num" json:"num"`
}

func Count(table string, qs *Qs) (num int) {
	wh, params := qs.Format()
	rows, err := Query(`
		select count(*) as num 
		from `+table+` a 
		`+wh, params...)
	if err != nil {
		panic(err)
	}
	var data []count
	err = scanner.ScanClose(rows, &data)
	if err != nil {
		panic(err)
	}
	if len(data) > 0 {
		num = data[0].Num
	}
	return
}

func Countsql(sql string, qs *Qs) (num int) {
	wh, params := qs.Format()
	rows, err := Query(sql+wh, params...)
	if err != nil {
		panic(err)
	}
	var data []count
	err = scanner.ScanClose(rows, &data)
	if err != nil {
		panic(err)
	}
	if len(data) > 0 {
		num = data[0].Num
	}
	return
}
func CountsqlC(sql string, params ...interface{}) (num int) {
	rows, err := Query(sql, params...)
	if err != nil {
		panic(err)
	}
	var data []count
	err = scanner.ScanClose(rows, &data)
	if err != nil {
		panic(err)
	}
	if len(data) > 0 {
		num = data[0].Num
	}
	return
}
